package com.mg.plugin.cropgrow.calculator;

import com.mg.plugin.cropgrow.entity.CropGrowData;
import com.mg.plugin.cropgrow.entity.StationData;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class CropGrowCalculator {

    public static List<CropGrowData> calcCropGrow(List<StationData> stationDataList, PlantType plantType) throws ParseException {

        Map<String, List<StationData>> stationDataMap = new HashMap<String, List<StationData>>();
        //计算当量温度，站点分类
        int year = calcStationET(stationDataList, stationDataMap);

        List<CropGrowData> cropGrowDataList = new ArrayList<CropGrowData>();

        for (String stationNum : stationDataMap.keySet()) {

            List<StationData> dataList = stationDataMap.get(stationNum);

            //时间不够，跳出计算
            if (dataList.size() != 183) {
                continue;
            }

            StationData stationData = dataList.get(0);

            CropGrowData cropGrowData = new CropGrowData(stationData, year, plantType);

            //计算相关要素值
            calcCropGrowData(cropGrowData, dataList, plantType);

            cropGrowDataList.add(cropGrowData);
        }

        return cropGrowDataList;
    }

    private static void calcCropGrowData(CropGrowData cropGrowData, List<StationData> stationDataList, PlantType plantType) throws ParseException {
        double fitTempTarget = 30;
        double dayOfEnd = 20;
        double peakRatio = 0.4;
        //站点排序
        List<StationData> dataList = sortStationDataByDate(stationDataList);

        //计算播种日期
        String plantDate = calcPlantDate(dataList, plantType);

        double equAccTemp = 0;

        for (StationData tmp : dataList) {
            equAccTemp += tmp.getEffectTemp();
        }

        double MH = 0.010886 * equAccTemp + 0.56;
        if (plantType == PlantType.B) {
            MH = MH - 0.6;
        }
        double JMLA = 0.171 * MH + 1.26;

        //直播计算方式应该有变化
        //TODO
        double leaf37 = calcLeaf12(0.37 * MH, 0.3 * MH);
        double leaf66 = calcLeaf14(0.66 * MH, 0.3 * MH);

        double TP = calcTP(dataList, leaf37, leaf66);

        double correctValue = 0.3504 * MH + 13.6;
        double TS = calcCorrectTS(correctValue, TP);

        cropGrowData.setPlantDay(plantDate);
        cropGrowData.setEquAccTemp(equAccTemp);
        cropGrowData.setCropLeafCount(MH);
        cropGrowData.setLeaf12(calcLeaf6(1.2));
        cropGrowData.setLeaf30(calcLeaf6(3.0));

        if (plantType == PlantType.A) {
            cropGrowData.setLeafFU(calcLeaf6(JMLA));
            cropGrowData.setFilmEffect(5.73 * MH + 20);
            cropGrowData.setRiceTrans(calcLeaf6(0.3 * MH));
        } else {
            cropGrowData.setLeafFU(0);
            cropGrowData.setFilmEffect(0);
            cropGrowData.setRiceTrans(0);
        }

        cropGrowData.setCorrectValue(correctValue);
        cropGrowData.setCorrectTSValue(TS);

        cropGrowData.setTiller(calcLeaf6(0.33 * MH));

        cropGrowData.setLeaf37(leaf37);
        cropGrowData.setLastLeaf(calcLeaf6(0.48 * MH));//无公式
        cropGrowData.setLeaf66(leaf66);
        cropGrowData.setValidTiller(3.41 * MH - 23.6);
        cropGrowData.setYoungEar(60 * MH - 299);
        cropGrowData.setEndLeaf(64.4 * MH - 135);
        cropGrowData.setEarStage(65.7 * MH - 56 + TS + 30);
        cropGrowData.setGroutLength(calcGroutLength(MH, TS, TP));
        cropGrowData.setBeginAccTemp(0.6 * MH + 11.6);
        cropGrowData.setFitTempTarget(fitTempTarget);
        cropGrowData.setDayOfEnd(dayOfEnd);
        cropGrowData.setPeakRatio(peakRatio);
        cropGrowData.setTp(TP);
        double ys = -0.67062 * Math.pow(TP - TS + 16.8, 2) + 24.636 * (TP - TS + 16.8) - 198.314;
        cropGrowData.setArgoYield(ys);

        cropGrowData.setStationDataList(dataList);
    }

    //计算种植日期
    private static String calcPlantDate(List<StationData> dataList, PlantType plantType) throws ParseException {
        double fitTemp = 12.3;
        if (plantType == PlantType.A) {
            fitTemp = 7;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        StationData[] stationData = {dataList.get(0), dataList.get(1), dataList.get(2), dataList.get(3), dataList.get(4), dataList.get(5), dataList.get(6), dataList.get(7), dataList.get(8)};
        for (int i = 9; i < dataList.size(); i++) {
            StationData data = calcFlushAvgTemp(stationData, fitTemp);
            if (data != null) {
                return sdfDate.format(sdf.parse(data.getDate()));
            } else {
                for (int j = 1; j < stationData.length; j++) {
                    stationData[j - 1] = stationData[j];
                }
                stationData[8] = dataList.get(i);
            }
        }
        return "1111-11-11";
    }

    private static StationData calcFlushAvgTemp(StationData[] stationData, double fitTemp) {
        for (int i = 0; i < 5; i++) {
            double totalTemp = 0;
            for (int j = i; j < i + 5; j++) {
                totalTemp += stationData[j].getAvgTemp();
            }
            if (totalTemp / 5 < fitTemp) {
                return null;
            }
        }
        for (StationData data : stationData) {
            if (data.getAvgTemp() >= fitTemp) {
                return data;
            }
        }
        return null;
    }

    private static double calcTP(List<StationData> dataList, double leaf37, double leaf66) {
        double totalTemp = 0;
        double tempCount = 0;
        double tmpEquAccTemp = 0;
        for (StationData tmp : dataList) {
            tmpEquAccTemp += tmp.getEffectTemp();
            if (tmpEquAccTemp >= leaf37 && tmpEquAccTemp <= leaf66) {
                totalTemp += tmp.getAvgTemp();
                tempCount++;
            }
        }
        return totalTemp / tempCount;
    }

    private static double calcGroutLength(double mh, double ts, double tp) {
        double ye = 42.7 * mh - 163.4;
        if (tp > 17) {
            return ye - ts * 0.8;
        }
        return ye - ts * 1.2;
    }

    private static double calcCorrectTS(double correctValue, double avgTemp) {
        return 60 / (1 + Math.pow(Math.E, -9.19024 * (correctValue - avgTemp) / 10)) - 30;
    }

    //排序
    private static List<StationData> sortStationDataByDate(List<StationData> dataList) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        StationData[] stationData = new StationData[dataList.size()];
        for (int i = 0; i < dataList.size(); i++) {
            stationData[i] = dataList.get(i);
        }
        //时间排序
        for (int i = 1; i < stationData.length; i++) {
            StationData tmp = stationData[i];//保存当前位置p的元素，其中[0,p-1]已经有序
            int j;
            for (j = i; j > 0 && sdf.parse(tmp.getDate()).before(sdf.parse(stationData[j - 1].getDate())); j--) {
                stationData[j] = stationData[j - 1];//后移一位
            }
            stationData[j] = tmp;//插入到合适的位置
        }
        List<StationData> resultList = new ArrayList<StationData>();
        for (StationData data : stationData) {
            resultList.add(data);
        }
        return resultList;
    }

    //计算1.2,3.0叶期
    private static double calcLeaf6(double leaf) {
        return (2.07 * leaf + 30.91) * leaf + 16.4;
    }

    //计算叶期 公式12
    private static double calcLeaf12(double leaf, double JMLA) {
        double ws = 7.587 * JMLA + 30.4 + 30;
        return Math.sqrt((leaf - JMLA) / 1.5) * ws + calcLeaf6(JMLA);
    }

    //计算叶期 公式14
    private static double calcLeaf14(double leaf, double JMLA) {
        double d0 = (0.456 * JMLA - 14.46) * JMLA + 60.91;
        return (2.2526 * leaf + 16.454) * leaf + 77.31 - d0 + 30;
    }

    /**
     * 计算当量温度，站点分类
     *
     * @param stationDataList 站点数据
     * @param stationDataMap  分类数据结够
     * @return
     */
    private static int calcStationET(List<StationData> stationDataList, Map<String, List<StationData>> stationDataMap) throws ParseException {
        int year = 0;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Calendar calendar = Calendar.getInstance();
        for (StationData stationData : stationDataList) {
            //计算ET
            stationData.setEffectTemp(calcET(stationData.getAvgTemp()));

            //站点分类
            if (stationDataMap.keySet().contains(stationData.getStationNum())) {
                stationDataMap.get(stationData.getStationNum()).add(stationData);
            } else {
                List<StationData> dataList = new ArrayList<StationData>();
                dataList.add(stationData);
                stationDataMap.put(stationData.getStationNum(), dataList);
            }

            if (year == 0) {
                //取当前年
                Date date = sdf.parse(stationData.getDate());
                calendar.setTime(date);
                year = calendar.get(Calendar.YEAR);
            }

        }

        return year;
    }

    /**
     * 当量温度计算公式
     *
     * @param temp
     * @return
     */
    private static double calcET(double temp) {
        double power = 9.19024 * Math.pow((temp - 26.1) / 26.7, 2) - 4.59512 * 0.22;
        return 15.603 / (1 + Math.pow(Math.E, power));
    }

}
